package archer.framework.common.repository.dao;

import archer.framework.common.ArcherCommonModule;
import archer.framework.common.repository.condition.OrderByItem;
import archer.framework.common.repository.condition.QueryCondition;
import archer.framework.common.repository.condition.QueryItem;
import archer.framework.common.repository.exception.DbException;
import archer.framework.common.repository.page.PageInfo;
import archer.framework.common.repository.support.ebean.page.EbeanPageConverter;
import archer.framework.core.constant.Constant;
import archer.framework.utils.ValidateUtils;
import com.avaje.ebean.*;
import com.avaje.ebeaninternal.server.core.DefaultServer;
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
import jodd.bean.BeanUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.*;

/**
 * @author christ
 * @date 2016/4/19
 */
@Component(value = "genericDao")
public class GenericDao {

    private static final Logger logger = LoggerFactory.getLogger(GenericDao.class);

    @Autowired
    protected EbeanServer db;

    /**
     * 获得id字段值
     * 解析@Id注解的字段值，非数据库查询
     *
     * @param entity
     * @param <T>
     * @return
     */
    public <T> T getBeanId(Object entity) {

        return (T) db.getBeanId(entity);
    }

    /**
     * 设置id字段值
     * 解析@Id注解的字段值，非数据库查询
     *
     * @param entity
     * @param id
     * @param <T>
     */
    public <T> void setBeanId(T entity, Object id) {

        DefaultServer server = (DefaultServer) db;

        BeanDescriptor<T> descriptor = (BeanDescriptor<T>) server.getBeanDescriptor(entity.getClass());

        descriptor.setBeanId(entity, id);
    }


    //----------------------------------------query----------------------------------------


    /**
     * 查找
     *
     * @param beanType
     * @param <T>
     * @return
     */
    public <T> Query<T> find(Class<T> beanType) {

        return db.find(beanType).setDisableLazyLoading(true);
    }

    /**
     * 根据queryCondition进行查询
     *
     * @param beanType
     * @param condition
     * @param <T>
     * @return
     */
    public <T> Query<T> find(Class<T> beanType, QueryCondition condition) {

        return find(find(beanType), condition);
    }

    /**
     * 根据queryCondition进行查询
     *
     * @param query
     * @param condition
     * @param <T>
     * @return
     */
    public <T> Query<T> find(Query<T> query, QueryCondition condition) {

        return findByCondition(query, condition);
    }

    /**
     * 根据主键查找
     *
     * @param beanType
     * @param id
     * @param <T>
     * @return
     */
    public <T> T findById(Class<T> beanType, Object id) {

        return find(beanType).setDisableLazyLoading(true).where().idEq(id).findUnique();
    }

    /**
     * 查找满足条件的一条数据
     *
     * @param beanType
     * @param <T>
     * @return
     */
    public <T> Query<T> findFirst(Class<T> beanType) {

        return findTop(beanType, 1);
    }

    /**
     * 查找满足条件的N条数据
     *
     * @param beanType
     * @param rows
     * @param <T>
     * @return
     */
    public <T> Query<T> findTop(Class<T> beanType, Integer rows) {

        return find(beanType).setFirstRow(0).setMaxRows(rows);
    }

    /**
     * 查找满足exists子查询的查询
     *
     * @param beanType
     * @param alias
     * @param subQuery
     * @param <T>
     * @return
     */
    public <T> Query<T> findExists(Class<T> beanType, String alias, Query subQuery) {
        return find(beanType).alias(alias).where().exists(subQuery).query();
    }

    /**
     * 查找满足notExists子查询的查询
     *
     * @param beanType
     * @param alias
     * @param subQuery
     * @param <T>
     * @return
     */
    public <T> Query<T> findNotExists(Class<T> beanType, String alias, Query subQuery) {
        return find(beanType).alias(alias).where().notExists(subQuery).query();
    }

    /**
     * 构造exists和notExists的子查询
     *
     * @param beanType
     * @param subAlias
     * @param rawSql
     * @param <T>
     * @return
     */
    public <T> Query<T> findExistsSubQuery(Class<T> beanType, String subAlias, String rawSql) {
        return find(beanType).alias(subAlias).select("id").where().raw(rawSql).query();
    }

    //----------------------------------------paged query----------------------------------------


    /**
     * 分页查询
     *
     * @param pageIndex
     * @param pageSize
     * @param query
     * @return
     */
    public PageInfo findPagedList(Integer pageIndex, Integer pageSize, Query query) {

        return pageInfo(query.setMaxRows(ebeanPageSize(pageSize)).setFirstRow(ebeanPageIndex(pageIndex) * ebeanPageSize(pageSize)).findPagedList());
    }

    /**
     * 分页查询
     *
     * @param pageIndex
     * @param pageSize
     * @param expressionList
     * @return
     */
    public PageInfo findPagedList(Integer pageIndex, Integer pageSize, ExpressionList expressionList) {

        return findPagedList(pageIndex, pageSize, expressionList.query());
    }

    /**
     * 分页查询
     *
     * @param query
     * @param condition
     * @param <T>
     * @return
     */
    public <T> PageInfo findPagedList(Query<T> query, QueryCondition condition) {

        if (ValidateUtils.isEmpty(condition)) {

            return findPagedList(null, null, findByCondition(query, condition));
        }

        return findPagedList(condition.getPageIndex(), condition.getPageSize(), findByCondition(query, condition));
    }

    /**
     * 将结果转换为PageInfo
     *
     * @param page
     * @return
     */
    public PageInfo pageInfo(Object page) {

        return EbeanPageConverter.instance().convert(page);
    }

    /**
     * 获得ebean的分页号
     *
     * @param pageIndex
     * @return
     */
    protected Integer ebeanPageIndex(Integer pageIndex) {

        if (ValidateUtils.isEmpty(pageIndex) || pageIndex <= 0) {
            return 0;
        }
        return pageIndex - 1;
    }

    /**
     * 获得ebean的分页大小
     *
     * @param pageSize
     * @return
     */
    protected Integer ebeanPageSize(Integer pageSize) {

        if (ValidateUtils.isEmpty(pageSize) || pageSize <= 0) {
            return ArcherCommonModule.defaultPageSize;
        }

        return pageSize;
    }

    /**
     * queryCondition转query
     *
     * @param query
     * @param condition
     * @param <T>
     * @return
     */
    protected <T> Query<T> findByCondition(Query<T> query, QueryCondition condition) {

        if (ValidateUtils.isEmpty(condition)) {
            return query;
        }

        if (ValidateUtils.isNotEmpty(condition.getQueryItems())) {

            ExpressionList<T> expressionList = query.where();
            for (QueryItem item : condition.getQueryItems()) {
                if (Constant.FilterRule.EQ.equals(item.getOp())) {
                    expressionList = expressionList.eq(item.getField(), item.getValue());
                    continue;
                }
                if (Constant.FilterRule.NE.equals(item.getOp())) {
                    expressionList = expressionList.ne(item.getField(), item.getValue());
                    continue;
                }
                if (Constant.FilterRule.GE.equals(item.getOp())) {
                    expressionList = expressionList.ge(item.getField(), item.getValue());
                    continue;
                }
                if (Constant.FilterRule.GT.equals(item.getOp())) {
                    expressionList = expressionList.gt(item.getField(), item.getValue());
                    continue;
                }
                if (Constant.FilterRule.IN.equals(item.getOp())) {
                    expressionList = expressionList.in(item.getField(), item.getValue());
                    continue;
                }
                if (Constant.FilterRule.LT.equals(item.getOp())) {
                    expressionList = expressionList.lt(item.getField(), item.getValue());
                    continue;
                }
                if (Constant.FilterRule.LE.equals(item.getOp())) {
                    expressionList = expressionList.le(item.getField(), item.getValue());
                    continue;
                }
                if (Constant.FilterRule.NIN.equals(item.getOp())) {
                    expressionList = expressionList.notIn(item.getField(), item.getValue());
                    continue;
                }
                if (Constant.FilterRule.LIKE.equals(item.getOp())) {
                    expressionList = expressionList.contains(item.getField(), String.valueOf(item.getValue()));
                    continue;
                }
                if (Constant.FilterRule.STARTS.equals(item.getOp())) {
                    expressionList = expressionList.startsWith(item.getField(), String.valueOf(item.getValue()));
                    continue;
                }
                if (Constant.FilterRule.ENDS.equals(item.getOp())) {
                    expressionList = expressionList.endsWith(item.getField(), String.valueOf(item.getValue()));
                    continue;
                }
            }
        }

        if (ValidateUtils.isNotEmpty(condition.getOrderByItems())) {
            OrderBy orderBy = new OrderBy();
            for (OrderByItem item : condition.getOrderByItems()) {
                if (Constant.OrderDirection.ASC.equals(item.getDirection())) {
                    orderBy.asc(item.getField());
                } else {
                    orderBy.desc(item.getField());
                }
            }
            query.setOrder(orderBy);
        }

        return query;
    }


    //-------------------------------------delete---------------------------------------

    /**
     * 逻辑删除
     *
     * @param bean
     * @param <T>
     */
    public <T> void logicalDelete(T bean) {

        if (ValidateUtils.isNotEmpty(bean)) {
            db.delete(bean);
        }
    }

    /**
     * 逻辑删除
     *
     * @param beans
     * @param <T>
     */
    public <T> void logicalDelete(Collection<T> beans) {

        if (ValidateUtils.isNotEmpty(beans)) {
            db.deleteAll(beans);
        }
    }

    /**
     * 删除
     *
     * @param clazz
     * @param id
     * @param <T>
     */
    public <T> void logicalDelete(Class<T> clazz, Object id) {

        if (ValidateUtils.isNotEmpty(id)) {
            db.delete(clazz, id);
        }
    }

    /**
     * 删除
     *
     * @param clazz
     * @param ids
     * @param <T>
     */
    public <T> void logicalDelete(Class<T> clazz, Collection<Object> ids) {

        if (ValidateUtils.isNotEmpty(ids)) {
            db.deleteAll(clazz, ids);
        }
    }


    /**
     * 删除
     *
     * @param bean
     * @param <T>
     */
    public <T> void delete(T bean) {

        if (ValidateUtils.isNotEmpty(bean)) {
            db.deletePermanent(bean);
        }
    }

    /**
     * 删除
     *
     * @param clazz
     * @param id
     * @param <T>
     */
    public <T> void delete(Class<T> clazz, Object id) {

        if (ValidateUtils.isNotEmpty(id)) {
            db.deletePermanent(clazz, id);
        }
    }

    /**
     * 删除
     *
     * @param beans
     * @param <T>
     */
    public <T> void delete(Collection<T> beans) {

        if (ValidateUtils.isNotEmpty(beans)) {
            db.deleteAllPermanent(beans);
        }
    }

    /**
     * 删除
     *
     * @param clazz
     * @param ids
     * @param <T>
     */
    public <T> void delete(Class<T> clazz, Collection<Object> ids) {

        if (ValidateUtils.isNotEmpty(ids)) {
            db.deleteAllPermanent(clazz, ids);
        }
    }


    //-------------------------------------store---------------------------------------


    /**
     * 保存或更新
     *
     * @param bean
     * @param <T>
     */
    public <T> void store(T bean) {

        if (ValidateUtils.isEmpty(bean)) {
            return;
        }


        //新增
        if (ValidateUtils.isEmpty(db.getBeanId(bean))) {
            db.insert(bean);
            return;
        }

        db.update(bean);
    }

    /**
     * 保存或更新
     *
     * @param beans
     * @param <T>
     */
    public <T> void store(Collection<T> beans) {

        if (ValidateUtils.isEmpty(beans)) {
            return;
        }

        List<T> insert = new ArrayList<>();
        List<T> update = new ArrayList<>();
        for (T each : beans) {
            if (ValidateUtils.isEmpty(getBeanId(each))) {
                insert.add(each);
            } else {
                update.add(each);
            }
        }
        db.insertAll(insert);
        db.updateAll(update);
    }

    /**
     * 保存或更新数据后同时查询返回数据
     *
     * @param bean
     * @param <T>
     * @return
     */
    public <T> T storeAndQuery(T bean) {

        if (ValidateUtils.isEmpty(bean)) {
            return null;
        }

        store(bean);

        return (T) findById(bean.getClass(), getBeanId(bean));
    }


    /**
     * 保存或更新数据后同时查询返回数据
     *
     * @param beans
     * @param <T>
     * @return
     */
    public <T> Collection<T> storeAndQuery(Collection<T> beans) {

        if (ValidateUtils.isEmpty(beans)) {
            return beans;
        }

        store(beans);

        Collection<T> result;
        try {
            result = beans.getClass().newInstance();
        } catch (Exception e) {
            logger.error(e.getMessage(), e);
            throw new DbException(e);
        }

        for (T bean : beans) {
            result.add((T) findById(bean.getClass(), getBeanId(bean)));
        }

        return result;
    }


    //-------------------------------------misc---------------------------------------


    /**
     * 判断是否被其他bean引用，这里判断的是未被逻辑删除的
     *
     * @param ref     被引用的对象的类型
     * @param fk      外键
     * @param fkValue 外键值
     * @param <T>
     * @return
     */
    public <T> boolean referencedBy(Class<T> ref, String fk, Object fkValue) {

        if (ValidateUtils.isEmpty(fkValue)) {
            return false;
        }

        return find(ref)
                .where()
                .eq(fk, fkValue)
                .findRowCount() > 0;
    }


    /**
     * 字段重复校验
     * <li>如：User表中mobile字段不能重复<li/>
     *
     * @param bean
     * @param field
     * @param <T>
     * @return
     */
    public <T> boolean duplicated(T bean, String field) {

        return duplicated(bean, "id", field);
    }

    /**
     * 字段重复校验
     * <li>如：User表中mobile字段不能重复<li/>
     *
     * @param bean
     * @param idField
     * @param field
     * @param <T>
     * @return
     */
    public <T> boolean duplicated(T bean, String idField, String field) {

        return duplicated(bean, idField, field, null);
    }

    /**
     * 字段重复校验
     * <li>如：User表中type=STAFF的mobile字段不能重复<li/>
     *
     * @param bean
     * @param field
     * @param rangeMap 数据范围过滤
     * @param <T>
     * @return
     */
    public <T> boolean duplicated(T bean, String field, Map rangeMap) {

        return duplicated(bean, "id", field, rangeMap);
    }


    /**
     * 字段重复校验
     * <li>如：User表中type=STAFF的mobile字段不能重复<li/>
     *
     * @param bean
     * @param idField  id字段
     * @param field    不能重复的判断字段
     * @param rangeMap 数据范围过滤
     * @param <T>
     * @return
     */
    public <T> boolean duplicated(T bean, String idField, String field, Map rangeMap) {

        if (ValidateUtils.isEmpty(bean)) {
            return false;
        }

        ExpressionList query = find(bean.getClass())
                .where()
                .ne(idField, db.getBeanId(bean))
                .eq(field, BeanUtil.pojo.getProperty(bean, field));

        if (ValidateUtils.isNotEmpty(rangeMap)) {

            query.allEq(rangeMap);
        }

        return query.findRowCount() > 0;

    }

    /**
     * sql语句类型的删除
     *
     * @param table
     * @return
     */
    public SqlUpdater sqlDelete(String table) {
        return new SqlUpdater().delete(table);
    }

    /**
     * sql语句类型逻辑删除
     *
     * @param table
     * @return
     */
    public SqlUpdater sqlLogicalDelete(String table) {
        return new SqlUpdater().logicalDelete(table);
    }

    //---------------------------------------------SqlUpdater---------------------------------------------

    /**
     * 内部类SqlUpdater
     */
    public class SqlUpdater {

        // 参数
        private Map<String, Object> params = new HashMap<>();

        private StringBuilder sb = new StringBuilder();

        /**
         * 逻辑删除
         *
         * @param table
         * @return
         */
        public SqlUpdater logicalDelete(String table) {
            sb.append(" update ").append(table).append(" ");
            sb.append(" set deleted=true ");
            return this;
        }

        /**
         * 物理删除
         *
         * @param table
         * @return
         */
        public SqlUpdater delete(String table) {
            sb.append(" delete from ").append(table).append(" ");
            return this;
        }

        /**
         * where
         *
         * @return
         */
        public SqlUpdater where() {
            sb.append(" where 1=1 ");
            return this;
        }

        /**
         * 设置参数值，默认操作符为=
         *
         * @param field
         * @param op
         * @param value
         * @return
         */
        public SqlUpdater setParam(String field, String op, Object value) {
            if (ValidateUtils.isNotEmpty(value)) {
                sb.append(" and ").append(field).append(" ").append(op).append(" :").append(field);
                params.put(field, value);
            }
            return this;
        }

        /**
         * 设置参数值
         *
         * @param field
         * @param value
         * @return
         */
        public SqlUpdater setParam(String field, Object value) {
            if (ValidateUtils.isNotEmpty(value)) {
                sb.append(" and ").append(field).append(" ").append("=").append(" :").append(field);
                params.put(field, value);
            }
            return this;
        }

        /**
         * 执行
         */
        public void execute() {
            // 至少有一个条件的时候执行，防止误删
            if (ValidateUtils.isNotEmpty(params)) {
                createSqlUpdate(sb.toString(), params).execute();
            }
        }

        /**
         * 创建SqlUpdate对象
         *
         * @param sql
         * @param param
         * @return
         */
        protected SqlUpdate createSqlUpdate(String sql, Map<String, Object> param) {

            Map realParam = new HashMap();
            String realSql = sql;

            for (Map.Entry entry : param.entrySet()) {
                if (ValidateUtils.isEmpty(entry.getValue())) {
                    realSql = processSql(realSql, (String) entry.getKey());
                } else {
                    realParam.put(entry.getKey(), entry.getValue());
                }
            }

            SqlUpdate sqlUpdate = db.createSqlUpdate(realSql);
            for (Map.Entry entry : param.entrySet()) {
                sqlUpdate.setParameter((String) entry.getKey(), entry.getValue());
            }
            return sqlUpdate;
        }

        /**
         * 对sql语句中出现的key=:key类似字段处理为1=1
         *
         * @param sql
         * @param key
         * @return
         */
        protected String processSql(String sql, String key) {
            return sql.replaceAll(key + "\\s*?(=|in|not|>|>=|<=|<|<>|like)\\s*?:" + key, " 1=1 ");
        }
    }
}
